﻿using Microsoft.Xaml.Behaviors;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace SharpSoft.WPF.Behaviors
{
    /// <summary>
    /// 为滚动视图提供动画
    /// </summary>
    public class AnimatedScrollViewerBehavior : Behavior<ScrollViewer>
    {

        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.ScrollChanged += AssociatedObject_ScrollChanged;
            this.HorizontalRatio = this.AssociatedObject.HorizontalOffset / this.AssociatedObject.ScrollableWidth;
            this.VerticalRatio = this.AssociatedObject.VerticalOffset / this.AssociatedObject.ScrollableHeight;
            this.EnsureCanScroll();
        }

        private void AssociatedObject_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            this.EnsureCanScroll();
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.ScrollChanged -= AssociatedObject_ScrollChanged;
        }


        /// <summary>
        /// 横向百分比位置
        /// </summary>
        public double HorizontalRatio
        {
            get { return (double)GetValue(HorizontalRatioProperty); }
            set { SetValue(HorizontalRatioProperty, value); }
        }

        public static readonly DependencyProperty HorizontalRatioProperty =
            DependencyProperty.Register("HorizontalRatio", typeof(double), typeof(AnimatedScrollViewerBehavior), new PropertyMetadata(0d));


        /// <summary>
        /// 纵向百分比位置
        /// </summary>
        public double VerticalRatio
        {
            get { return (double)GetValue(VerticalRatioProperty); }
            set { SetValue(VerticalRatioProperty, value); }
        }

        public static readonly DependencyProperty VerticalRatioProperty =
            DependencyProperty.Register("VerticalRatio", typeof(double), typeof(AnimatedScrollViewerBehavior), new PropertyMetadata(0d));



        /// <summary>
        /// 能否往左滚动
        /// </summary>
        public bool CanScrollLeft
        {
            get { return (bool)GetValue(CanScrollLeftProperty); }
            set { SetValue(CanScrollLeftProperty, value); }
        }

        public static readonly DependencyProperty CanScrollLeftProperty =
            DependencyProperty.Register("CanScrollLeft", typeof(bool), typeof(AnimatedScrollViewerBehavior), new PropertyMetadata(false));


        /// <summary>
        /// 能否往右滚动
        /// </summary>
        public bool CanScrollRight
        {
            get { return (bool)GetValue(CanScrollRightProperty); }
            set { SetValue(CanScrollRightProperty, value); }
        }

        public static readonly DependencyProperty CanScrollRightProperty =
            DependencyProperty.Register("CanScrollRight", typeof(bool), typeof(AnimatedScrollViewerBehavior), new PropertyMetadata(false));


        /// <summary>
        /// 能否往上滚动
        /// </summary>
        public bool CanScrollTop
        {
            get { return (bool)GetValue(CanScrollTopProperty); }
            set { SetValue(CanScrollTopProperty, value); }
        }

        public static readonly DependencyProperty CanScrollTopProperty =
            DependencyProperty.Register("CanScrollTop", typeof(bool), typeof(AnimatedScrollViewerBehavior), new PropertyMetadata(false));


        /// <summary>
        /// 能否往下滚动
        /// </summary>
        public bool CanScrollBottom
        {
            get { return (bool)GetValue(CanScrollBottomProperty); }
            set { SetValue(CanScrollBottomProperty, value); }
        }

        public static readonly DependencyProperty CanScrollBottomProperty =
            DependencyProperty.Register("CanScrollBottom", typeof(bool), typeof(AnimatedScrollViewerBehavior), new PropertyMetadata(false));

        /// <summary>
        /// 确认当前状态能否滚动
        /// </summary>
        private void EnsureCanScroll()
        {
            var sv = this.AssociatedObject;
            if (sv.HorizontalOffset <= 0)
            {

            }
            this.CanScrollLeft = (sv.HorizontalOffset > 0);
            this.CanScrollRight = (sv.HorizontalOffset < sv.ScrollableWidth);
            this.CanScrollTop = (sv.VerticalOffset > 0);
            this.CanScrollBottom = (sv.VerticalOffset < sv.ScrollableHeight);
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (this.AssociatedObject != null)
            {
                if (e.Property == HorizontalRatioProperty)
                {
                    if (!double.IsNaN(this.HorizontalRatio))
                    {
                        var offset = this.HorizontalRatio * this.AssociatedObject.ScrollableWidth;
                        this.AssociatedObject.ScrollToHorizontalOffset(offset);
                    }

                }
                else if (e.Property == VerticalRatioProperty)
                {
                    if (!double.IsNaN(this.VerticalRatio))
                    {
                        var offset = this.VerticalRatio * this.AssociatedObject.ScrollableHeight;
                        this.AssociatedObject.ScrollToVerticalOffset(offset);
                    }
                }
            }

        }

        public void PageTop(TimeSpan duration)
        {
            var sv = this.AssociatedObject;

            if (sv.ScrollableHeight == 0)
            {
                return;
            }
            var from = sv.VerticalOffset / sv.ScrollableHeight;
            var to = Math.Max(sv.VerticalOffset - sv.ViewportHeight, 0) / sv.ScrollableHeight;

            DoubleAnimation da = new DoubleAnimation(from, to, duration);

            this.BeginAnimation(VerticalRatioProperty, da);
        }
        public void PageBottom(TimeSpan duration)
        {
            var sv = this.AssociatedObject;

            if (sv.ScrollableHeight == 0)
            {
                return;
            }
            var from = sv.VerticalOffset / sv.ScrollableHeight;
            var to = Math.Min(sv.VerticalOffset + sv.ViewportHeight, sv.ScrollableHeight) / sv.ScrollableHeight;

            DoubleAnimation da = new DoubleAnimation(from, to, duration);

            this.BeginAnimation(VerticalRatioProperty, da);
        }

        public void PageRight(TimeSpan duration)
        {

            var sv = this.AssociatedObject;

            if (sv.ScrollableWidth == 0)
            {
                return;
            }
            var from = sv.HorizontalOffset / sv.ScrollableWidth;
            var to = Math.Min(sv.HorizontalOffset + sv.ViewportWidth, sv.ScrollableWidth) / sv.ScrollableWidth;

            DoubleAnimation da = new DoubleAnimation(from, to, duration);

            this.BeginAnimation(HorizontalRatioProperty, da);
        }
        public void PageLeft(TimeSpan duration)
        {
            var sv = this.AssociatedObject;

            if (sv.ScrollableWidth == 0)
            {
                return;
            }
            var from = sv.HorizontalOffset / sv.ScrollableWidth;
            var to = Math.Max(sv.HorizontalOffset - sv.ViewportWidth, 0) / sv.ScrollableWidth;

            DoubleAnimation da = new DoubleAnimation(from, to, duration);

            this.BeginAnimation(HorizontalRatioProperty, da);
        }

        public Storyboard ScrollTo(TimeSpan duration, double? horizontalRatio, double? verticalRatio)
        {
            Storyboard sb = new Storyboard() { FillBehavior = FillBehavior.Stop };
            if (horizontalRatio.HasValue)
            {
                DoubleAnimation dah = new DoubleAnimation(horizontalRatio.Value, duration);
                Storyboard.SetTarget(dah, this);
                Storyboard.SetTargetProperty(dah, new PropertyPath(HorizontalRatioProperty.Name));
                sb.Children.Add(dah);
            }
            if (verticalRatio.HasValue)
            {
                DoubleAnimation dav = new DoubleAnimation(verticalRatio.Value, duration);
                Storyboard.SetTarget(dav, this);
                Storyboard.SetTargetProperty(dav, new PropertyPath(VerticalRatioProperty.Name));
                sb.Children.Add(dav);
            }
            sb.Begin();
            return sb;
        }
    }
}
